/**
 * @class dfly.Module
 * @constructor
 * @params  {String}    moduleName     name of the module
 */
dfly.Module = function(module) {
   
   /**
    * This module name
    */
   this.moduleName = module.path;
   
   /**
    * Incoming wires
    */
   this.incomingWires = [];
   
   /**
    * Outcoming wires
    */
   this.outcomingWires = [];
   
   /**
    * The base configuration 
    */
   this.baseConfig = {};
   
   /**
    * Parent Module
    */
   this.parentModule = null;
   
   if(module.definition) {
      this.parseDefinition(module.definition);
   }
};

dfly.Module.prototype = {
   
   /**
    * Parse definition for a definition module
    */
   parseDefinition: function(definition) {
      
      // Instanciate the submodules
      this.subModules = [];
      for(var i = 0 ; i < definition.containers.length ; i++) {
         
         var containerConfig = definition.containers[i];
         this.subModules[i] = new dfly.Module( dfly.ModuleModel.get(containerConfig.moduleName) );
         this.subModules[i].baseConfig = containerConfig.formValue;
         
         // For the outputs:
         this.subModules[i].parentModule = this;
      }


      // Create the "wires"
      for(i = 0 ; i < definition.wires.length ; i++) {
         var wireDef = definition.wires[i];
         var wire = {
            outputName:  wireDef.src.terminalName,
            inputName: wireDef.tgt.terminalName,
            src: this.subModules[wireDef.src.moduleId],
            tgt: this.subModules[wireDef.tgt.moduleId]
         };
         this.subModules[wireDef.src.moduleId].outcomingWires.push(wire);
         this.subModules[wireDef.tgt.moduleId].incomingWires.push(wire);
      }
      
   },
   
   /**
    * Unique function to run both types of modules
    */
   run: function(config) {
      console.log(this.moduleName, "runs with config", config);
      
      
      if(this.moduleName == "/system/input") {
         this.setOutput("output_value", (config.hasOwnProperty("value")) ? config.value : config.type.defaultValue);
      }
      else if(this.moduleName == "/system/output") {
         if(this.parentModule) {
            //console.log("my parent is ", this.parentModule);
            this.parentModule.setOutput("output_"+config.name, config.hasOwnProperty("value") ? config["value"] : config["input_value"]);
            //console.log("run output !!!");
         }
      }
      else if(this.moduleName == "/system/jsFunction") {
         try {
            var f = eval('('+config.code+')');
            f.call(this,config.parameters);
         }
         catch(ex) {
            console.log("Error executing "+this.moduleName+": ", ex, " with config ", config);
         }
      }
      else {
         this.runDefinition(config);
      }
   },
   
   /**
    * Execute a composed module
    */
   runDefinition: function(config) {
      
      // Run all input modules
      for(var i = 0 ; i < this.subModules.length ; i++) {
         var module = this.subModules[i];
         if( module.moduleName == "/system/input" ) {
            module.run( module.getRunParams(config) );
         }
      }
      
      // Run modules that have no input Wires 
      for(var i = 0 ; i < this.subModules.length ; i++) {
         var module = this.subModules[i];
         if( module.moduleName != "/system/input" ) {
            if(module.incomingWires.length == 0) {
               module.run( module.getRunParams(config) );
            }
         }
      }
      
   },
   
   /**
    * This function mix the data from the baseConfig and the incoming wires
    */
   getRunParams: function(config) {
      
      var params = {};
      
      // Get the baseConfig parameters
      for(var key in this.baseConfig ) {
         if(this.baseConfig.hasOwnProperty(key)) {
            params[key] = this.baseConfig[key];
         }
      }
      
      // Get the incomingWires parameters
      for(var k = 0 ; k < this.incomingWires.length ; k++) {
         var w = this.incomingWires[k];
         var paramName = w.inputName.substr(6,w.inputName.length-6);
         
         // If the param name is the value of an element in the array:
         var isArrayParam = paramName.match(/(.*)\[([0-9]*)\]$/);
         if(isArrayParam) {
            var arrayName = isArrayParam[1];
            var index = parseInt(isArrayParam[2]);
            params[arrayName][index] = w.value;
         }
         else {
            params[paramName] = w.value;
         }
      }
      
      // Paramètres passés par le config du run parent
      if(this.moduleName == '/system/input') {
         if(config && config.hasOwnProperty(this.baseConfig.name) ) {
            params["value"] = config[this.baseConfig.name];
         }
      }
      
      return params;
   },
   
   isRunable: function() {
      for(var i = 0 ; i < this.incomingWires.length ; i++) {
         if( !this.incomingWires[i].value ) {
            return false;
         }
      }
      return true;
   },
   
   setOutput: function(outputName, value) {
      
      // for each outcomingWires where outcomingWires.outputName = outputName 
      for(var i = 0 ; i < this.outcomingWires.length ; i++) {
         var wire = this.outcomingWires[i];
         
         //console.log("comparing", wire.outputName, outputName);
         if( wire.outputName == outputName ) {
            
            // Set the value of those wires
            wire.value = value;
            
            // Run the target if possible.
            if( wire.tgt.isRunable() ) {
               wire.tgt.run( wire.tgt.getRunParams() );
            }
            
         }
      }
      
   }
   
   
};

